Welcome to the ATFS (Alliance for Tropical Research Science) data harmonization app!

1 Intro

The app is a tool meant to be used by 2 or more networks that are planing on combining their data for a common analysis.

1.1 Profiles

The app relies on “Profiles” that indicate how the data is stored in the file(s) provided: names of columns storing the DBH, the census ID, the tree tag, units of measurements etc…

A profile is a .rds file that is downloaded via the app once all the information about the data has been provided in the Headers and Units tab of the app.

One same profile can be uploaded as “input profile” in the Headers and Units tab, to speed up the process once your network’s data has been profiled, and/or as “output profile” in the Output format tab, to transform other networks’ data into that profile.

Some networks have their profile stored within the app.

1.2 Getting your data ready

The app only accepts CSV files.

It performs best if all the information that you want to share is collated into one analytical file, so we recommend that you append your species and plot information to your measurement information beforehand, and upload that one bigger file into the app.

That said, you can decide to utilize the app to do exactly that. There is no limit to the number of files you can upload but they all need to connect to each other in one way or another, so that by a stacking and/or merging them, it is possible to collate them down to one file. We will get to this in more detail in a moment.

The app also relied on tidy data, which means that every column is a variable, every row is an observation and every cell is a single value. For example, a data set with multiple column for the DBH measurement (e.g. DBH_2015, DBH_2020 etc…) is not a tidy data set. Instead, there should be a column for the variable year (which, in our example, will take a value of 2015 or 2020), and a column for DBH. If your data is not in a tidy format, the Tidy table tab will help you reshape your data.

1.3 R package

The app relies on functions that are maintained in a GitHub R package located here: https://github.com/Alliance-for-Tropical-Forest-Science/DataHarmonization.

1.4 Getting the app to start

1.4.1 Running the app on your local machine

We recommend to run the app on your local machine (via R and RStudio) if one of the following cases apply to you:

  • You have poor internet connection
  • You are working with large data files
  • You are familiar with the development of Shiny apps and would like to troubleshoot any issues you may encounter yourself

To open the app in R, you will need to install the DataHarmonization R package and launch Shiny with the following lines of code.

# install the R package

devtools::install_github("Alliance-for-Tropical-Forest-Science/DataHarmonization", build_vignettes = TRUE)

# run the app

shiny::runGitHub( "Alliance-for-Tropical-Forest-Science/DataHarmonization", subdir = "inst/app")

Note that you may need to install devtools package first and that installing the DataHarmonization R package may ask you to update a list packages.

You’ll want to re-install the package every once in a while, to get the latest version of the app.

1.4.2 Running the app online

If you don’t have R and RStudio and if your data is not too big, you can choose to run the online version of the app by clicking on this link. Note that online version may be lagging behind the GitHub version.

2 Interacting with the app

Once the app is launched you can start interacting with it.

There are multiple tabs to go through. Some tabs will be skipped automatically if they don’t apply to your situation and you may skip others if you don’t need/want them.

When you land on a tab, always advance with an action button (even if skipping) so your inputs are taken into account. You may use the navigation panel to return to a previous tab but remember to click on an action button to save your updated entries.

2.1 Upload your file(s)

This tab starts with information that we already covered in the intro. The checklist is only a guideline to help you getting ready, and you don’t actually need to check the boxes to keep going.

The numbered tasks are the elements that you do need to complete to be able to move forward.

  1. Indicate how many tables you wish to upload

  2. Indicate the finest level of measurement in your data:

    • Plot: if your data only consists of plot level measurements like species richness, total basal area, total number of stems etc…
    • Species: if your data consists of species level measurements like abundance, basal area etc… This does not prevent you from also uploading plot level information that is stored in a separate file.
    • Tree: if your data consists of tree diameters, circumference,… and you are only measuring the main stem of each tree. This does not prevent you from also uploading plot and species level information if they are stored in separate file(s).
    • Stem: if your data consists of stem diameters, circumference,… and some stems belong to a same tree. This does not prevent you from uploading plot and species level information if they are stored in a separate file(s).

Again, even if you are uploading plot level information but have a stem level data, you should upload that file along and indicate that your level of measurement is “Stem”.

  1. Upload you tables. You’ll have as many upload boxes as you indicated needing in step 1. For each of them:

    • click on Browse... and navigate to the csv file you want to upload.
    • Type a more meaningful name to replace the generic “Table1”, “Table2” etc… This is particularly useful if you are uploading more than one file.
    • Check on the right hand side that the columns and rows of your data are rendering properly.
    • In the unlikely event that your tables are not rendering properly, adjust the parameters (separator and header) by clicking on the little gear icon .
  2. Click on SUBMIT to proceed to the next step.

2.2 Stack tables

If you uploaded more than one table, you will be prompted to the Stack tables tab, but this tab will be skipped if you only uploaded one table.

You will need to stack 2 or more tables if you are collecting the same information in multiple files. This can be the case if, for example, you are keeping your measurements from different plots in different files. Or you are keeping one file per census.

If you don’t need to stack tables, click on SKIP THIS STEP.

It is important that the files you are stacking have the same set of columns.

  1. Select all the tables that need to be stacked

  2. Click on STACK TABLES

  3. Double check your newly created table looks ok

  4. Click on GO TO MERGE to proceed to the next step. (Note: if you are down to one table at this stage the button’s label will change, so click SKIP MERGING SINCE ALL YOUR DATA IS NOW STACKED).

2.3 Merge tables

If you uploaded more than one table and not all of them were stacked, you will be prompted to the Merge tables tab, but this tab will be skipped if you only uploaded one table, or if all your tables were stacked.

At the end of this stage you have to be down to one table.

You need to use merging if, e.g., your species or your plot information is stored in a different table than your measurement table, and there is at least one “key” column that you can use to connect the tables together.

  1. In Merge this table, select the main measurement table (the one onto which you want to merge extra information into, from other tables). Note that this may be your now stacked table.

  2. In And this table, Select the table that you want to bring information from.

  3. Click on both blue arrows.

  4. In the two dropdown menus about the ‘KEY column(s)’, select the column(s) that allow to connect the to tables together.Select all columns that are common between tables, otherwise columns will be repeated in the output, with extension ‘.y’ in the name of the second table.

  5. click on ‘MERGE TABLES’.

  6. If you are still not down to one table, another box will appear. Repeat 1-5 with the remaining tables.

  7. click on ‘GO TO TIDY’.

2.4 Tidy table

At this stage, we want to make sure your data has one row per observation and one variable per column.

If you collected the same type of information in several columns (e.g. you added a column each time you visited a tree, or for each stem of the tree etc…), you need to “tidy” your table (also called wide-to-long reshaping).

  1. In the top-most box, use the radio-buttons to indicate the reason you added new columns for a new observation.

  2. The next set of boxes are pre-filled with our best guesses at the columns that may contain the same variable (columns that have similar names like dbh1, dbh2, or year1, year2…). Our guess may be terrible. Your role is to:

    1. Indicate the name of the new column that you which your variable to be called (e.g. dbh) in the text box. Note that this should start by a letter and have no space.

    2. Select all the columns of your data that represent the variable indicated in step a. (e.g dbh1 and dbh2) using the drop-down menu.

    3. Tick the little tick-box on the upper-left corner of the box, to indicated that you do want to take into account what you selected.

  3. Repeat a-c for the next variable(s), e.g. you may need a box to indicate year in the text box and year1 and year2 in the drop-down menu. Don’t forget to tick the tick-box for those variables too.

  4. Click on ‘TIDY’

  5. Click on ‘GO TO HEADERS’

2.5 Headers and Units

Here, we want to know in what column some key information is stored.

  1. If…

    • A. … your data follows one of our pre-loaded standards: You can select it to help filing out some of the general information. But you should double check that accurately describe your particular data set.
    • B. … you have already gone through this step and saved your profile (.rds file): You can upload your profile. Double check that the information is filled out properly.
    • C. …This is your first time on this page: You have to go through all the drop-down menus:
      • column 1: Go through this column before indicating more information in column 2. For each element, select the name of the field of your data, if any, that corresponds the best to what is asked. Leave “none” if none of your variable apply.
      • column 2: Complete the information of that column after you are done with the information of column 1, because some field may appear/disappear depending on what you entered in 1.
      • Once you are done, save your profile (a .rds file) so you won’t need to go through all of this again (see B. above)
  2. click on ‘APPLY CHANGES’ and read any warnings that may popup, adjust your entries if possible (it is okay to ignore warnings) and re-apply your changes. Save (or update if needed) your profile (.rds file)

  3. Check your new formated data looks ok. The headers and units are now following ATFS’s standard. You can see what those are by clicking the little button.

  4. click on ‘NEXT’

2.6 Codes

This stage is only required if you indicated column(s) for tree codes in the Tree Measurement section of the previous tab.

The table shows the list of codes that are available in the column(s) you indicated. If you intend to translate these codes to match the ones of another profile (which you will be able to do at a later step), or vice-versa, you need to fill this table out.

Once you are done with the table you can update your profile (by downloading and overwriting your .rds file). If you have already saved your profile after this step and used it to fill out the previous step, you can click on the “Use your profile” button to automatically fill the table.

There is a list of predefined definitions, which, if used by you and your collaborator, will helps automatically translating your codes. But if you can’t find a definition that matches yours, just type your own.

2.7 Corrections

2.8 Output format

2.9 Visualise results

2.10 Download

2.11 Help

LS0tCnRpdGxlOiAiRGF0YSBIYXJtb25pemF0aW9uIEFwcCBUdXRvcmlhbCIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQnKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShmb250YXdlc29tZSkKYGBgIAoKCgoKV2VsY29tZSB0byB0aGUgQVRGUyAoQWxsaWFuY2UgZm9yIFRyb3BpY2FsIFJlc2VhcmNoIFNjaWVuY2UpIGRhdGEgaGFybW9uaXphdGlvbiBhcHAhCgoKIyBJbnRybyB7I2ludHJvfQoKVGhlIGFwcCBpcyBhIHRvb2wgbWVhbnQgdG8gYmUgdXNlZCBieSAyIG9yIG1vcmUgbmV0d29ya3MgdGhhdCBhcmUgcGxhbmluZyBvbiBjb21iaW5pbmcgdGhlaXIgZGF0YSBmb3IgYSBjb21tb24gYW5hbHlzaXMuCgojIyBQcm9maWxlcyB7I3Byb2ZpbGV9CgpUaGUgYXBwIHJlbGllcyBvbiAiUHJvZmlsZXMiIHRoYXQgaW5kaWNhdGUgaG93IHRoZSBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgZmlsZShzKSBwcm92aWRlZDogbmFtZXMgb2YgY29sdW1ucyBzdG9yaW5nIHRoZSBEQkgsIHRoZSBjZW5zdXMgSUQsIHRoZSB0cmVlIHRhZywgdW5pdHMgb2YgbWVhc3VyZW1lbnRzIGV0Yy4uLgoKQSBwcm9maWxlIGlzIGEgLnJkcyBmaWxlIHRoYXQgaXMgZG93bmxvYWRlZCB2aWEgdGhlIGFwcCBvbmNlIGFsbCB0aGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRhdGEgaGFzIGJlZW4gcHJvdmlkZWQgaW4gdGhlIFtgSGVhZGVycyBhbmQgVW5pdHNgXSgjSGVhZGVycykgdGFiIG9mIHRoZSBhcHAuCgpPbmUgc2FtZSBwcm9maWxlIGNhbiBiZSB1cGxvYWRlZCBhcyAiaW5wdXQgcHJvZmlsZSIgaW4gdGhlIFtgSGVhZGVycyBhbmQgVW5pdHNgXSgjSGVhZGVycykgdGFiLCB0byBzcGVlZCB1cCB0aGUgcHJvY2VzcyBvbmNlIHlvdXIgbmV0d29yaydzIGRhdGEgaGFzIGJlZW4gcHJvZmlsZWQsIGFuZC9vciBhcyAib3V0cHV0IHByb2ZpbGUiIGluIHRoZSBbYE91dHB1dCBmb3JtYXRgXSgjT3V0cHV0Rm9ybWF0KSB0YWIsIHRvIHRyYW5zZm9ybSBvdGhlciBuZXR3b3JrcycgZGF0YSBpbnRvIHRoYXQgcHJvZmlsZS4KClNvbWUgbmV0d29ya3MgaGF2ZSB0aGVpciBwcm9maWxlIHN0b3JlZCB3aXRoaW4gdGhlIGFwcC4KCiMjIEdldHRpbmcgeW91ciBkYXRhIHJlYWR5IHsjcHJlcGRhdGF9CgpUaGUgYXBwIG9ubHkgYWNjZXB0cyBDU1YgZmlsZXMuIAoKSXQgcGVyZm9ybXMgYmVzdCBpZiBhbGwgdGhlIGluZm9ybWF0aW9uIHRoYXQgeW91IHdhbnQgdG8gc2hhcmUgaXMgY29sbGF0ZWQgaW50byBvbmUgYW5hbHl0aWNhbCBmaWxlLCBzbyB3ZSByZWNvbW1lbmQgdGhhdCB5b3UgYXBwZW5kIHlvdXIgc3BlY2llcyBhbmQgcGxvdCBpbmZvcm1hdGlvbiB0byB5b3VyIG1lYXN1cmVtZW50IGluZm9ybWF0aW9uIGJlZm9yZWhhbmQsIGFuZCB1cGxvYWQgdGhhdCBvbmUgYmlnZ2VyIGZpbGUgaW50byB0aGUgYXBwLgoKVGhhdCBzYWlkLCB5b3UgY2FuIGRlY2lkZSB0byB1dGlsaXplIHRoZSBhcHAgdG8gZG8gZXhhY3RseSB0aGF0LiBUaGVyZSBpcyBubyBsaW1pdCB0byB0aGUgbnVtYmVyIG9mIGZpbGVzIHlvdSBjYW4gdXBsb2FkIGJ1dCB0aGV5IGFsbCBuZWVkIHRvIGNvbm5lY3QgdG8gZWFjaCBvdGhlciBpbiBvbmUgd2F5IG9yIGFub3RoZXIsIHNvIHRoYXQgYnkgYSBzdGFja2luZyBhbmQvb3IgbWVyZ2luZyB0aGVtLCBpdCBpcyBwb3NzaWJsZSB0byBjb2xsYXRlIHRoZW0gZG93biB0byBvbmUgZmlsZS4gV2Ugd2lsbCBnZXQgdG8gdGhpcyBpbiBtb3JlIGRldGFpbCBpbiBhIG1vbWVudC4KClRoZSBhcHAgYWxzbyByZWxpZWQgb24gW3RpZHldKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90aWR5ci92aWduZXR0ZXMvdGlkeS1kYXRhLmh0bWwjOn46dGV4dD1UaWR5JTIwZGF0YSUyMGlzJTIwYSUyMHN0YW5kYXJkLEV2ZXJ5JTIwY29sdW1uJTIwaXMlMjBhJTIwdmFyaWFibGUuKSBkYXRhLCB3aGljaCBtZWFucyB0aGF0IGV2ZXJ5IGNvbHVtbiBpcyBhIHZhcmlhYmxlLCBldmVyeSByb3cgaXMgYW4gb2JzZXJ2YXRpb24gYW5kIGV2ZXJ5IGNlbGwgaXMgYSBzaW5nbGUgdmFsdWUuIEZvciBleGFtcGxlLCBhIGRhdGEgc2V0IHdpdGggbXVsdGlwbGUgY29sdW1uIGZvciB0aGUgREJIIG1lYXN1cmVtZW50IChlLmcuIERCSF8yMDE1LCBEQkhfMjAyMCBldGMuLi4pIGlzIG5vdCBhIHRpZHkgZGF0YSBzZXQuIEluc3RlYWQsIHRoZXJlIHNob3VsZCBiZSBhIGNvbHVtbiBmb3IgdGhlIHZhcmlhYmxlIGB5ZWFyYCAod2hpY2gsIGluIG91ciBleGFtcGxlLCB3aWxsIHRha2UgYSB2YWx1ZSBvZiAyMDE1IG9yIDIwMjApLCBhbmQgYSBjb2x1bW4gZm9yIGBEQkhgLgpJZiB5b3VyIGRhdGEgaXMgbm90IGluIGEgdGlkeSBmb3JtYXQsIHRoZSBbYFRpZHkgdGFibGVgXSgjVGlkeWluZykgdGFiIHdpbGwgaGVscCB5b3UgcmVzaGFwZSB5b3VyIGRhdGEuIAoKCiMjIFIgcGFja2FnZSB7I3BhY2thZ2V9CgpUaGUgYXBwIHJlbGllcyBvbiBmdW5jdGlvbnMgdGhhdCBhcmUgbWFpbnRhaW5lZCBpbiBhIEdpdEh1YiBSIHBhY2thZ2UgbG9jYXRlZCBoZXJlOiBodHRwczovL2dpdGh1Yi5jb20vQWxsaWFuY2UtZm9yLVRyb3BpY2FsLUZvcmVzdC1TY2llbmNlL0RhdGFIYXJtb25pemF0aW9uLgoKIyMgR2V0dGluZyB0aGUgYXBwIHRvIHN0YXJ0IHsjc3RhcnR9CgojIyMgUnVubmluZyB0aGUgYXBwIG9uIHlvdXIgbG9jYWwgbWFjaGluZSB7I2xvY2FsUnVufQoKV2UgcmVjb21tZW5kIHRvIHJ1biB0aGUgYXBwIG9uIHlvdXIgbG9jYWwgbWFjaGluZSAodmlhIFIgYW5kIFJTdHVkaW8pIGlmIG9uZSBvZiB0aGUgZm9sbG93aW5nIGNhc2VzIGFwcGx5IHRvIHlvdToKCiAtIFlvdSBoYXZlIHBvb3IgaW50ZXJuZXQgY29ubmVjdGlvbgogLSBZb3UgYXJlIHdvcmtpbmcgd2l0aCBsYXJnZSBkYXRhIGZpbGVzCiAtIFlvdSBhcmUgZmFtaWxpYXIgd2l0aCB0aGUgZGV2ZWxvcG1lbnQgb2YgU2hpbnkgYXBwcyBhbmQgd291bGQgbGlrZSB0byB0cm91Ymxlc2hvb3QgYW55IGlzc3VlcyB5b3UgbWF5IGVuY291bnRlciB5b3Vyc2VsZgogClRvIG9wZW4gdGhlIGFwcCBpbiBSLCB5b3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgdGhlIERhdGFIYXJtb25pemF0aW9uIFIgcGFja2FnZSBhbmQgbGF1bmNoIFNoaW55IHdpdGggdGhlIGZvbGxvd2luZyBsaW5lcyBvZiBjb2RlLgoKYGBge3J9CiMgaW5zdGFsbCB0aGUgUiBwYWNrYWdlCgpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkFsbGlhbmNlLWZvci1Ucm9waWNhbC1Gb3Jlc3QtU2NpZW5jZS9EYXRhSGFybW9uaXphdGlvbiIsIGJ1aWxkX3ZpZ25ldHRlcyA9IFRSVUUpCgojIHJ1biB0aGUgYXBwCgpzaGlueTo6cnVuR2l0SHViKCAiQWxsaWFuY2UtZm9yLVRyb3BpY2FsLUZvcmVzdC1TY2llbmNlL0RhdGFIYXJtb25pemF0aW9uIiwgc3ViZGlyID0gImluc3QvYXBwIikKYGBgCgpOb3RlIHRoYXQgeW91IG1heSBuZWVkIHRvIGluc3RhbGwgYGRldnRvb2xzYCBwYWNrYWdlIGZpcnN0IGFuZCB0aGF0IGluc3RhbGxpbmcgdGhlIERhdGFIYXJtb25pemF0aW9uIFIgcGFja2FnZSBtYXkgYXNrIHlvdSB0byB1cGRhdGUgYSBsaXN0IHBhY2thZ2VzLgoKKipZb3UnbGwgd2FudCB0byByZS1pbnN0YWxsIHRoZSBwYWNrYWdlIGV2ZXJ5IG9uY2UgaW4gYSB3aGlsZSwgdG8gZ2V0IHRoZSBsYXRlc3QgdmVyc2lvbiBvZiB0aGUgYXBwLioqCgojIyMgUnVubmluZyB0aGUgYXBwIG9ubGluZSB7I29ubGluZVJ1bn0KCklmIHlvdSBkb24ndCBoYXZlIFIgYW5kIFJTdHVkaW8gYW5kIGlmIHlvdXIgZGF0YSBpcyBub3QgdG9vIGJpZywgeW91IGNhbiBjaG9vc2UgdG8gcnVuIHRoZSBvbmxpbmUgdmVyc2lvbiBvZiB0aGUgYXBwIGJ5IGNsaWNraW5nIG9uIHRoaXMgW2xpbmtdKGh0dHBzOi8vdmFsZW50aW5laGVyci5zaGlueWFwcHMuaW8vVG1GT19BY2NlbE5ldC8pLiAKTm90ZSB0aGF0IG9ubGluZSB2ZXJzaW9uIG1heSBiZSBsYWdnaW5nIGJlaGluZCB0aGUgR2l0SHViIHZlcnNpb24uIAoKCiMgSW50ZXJhY3Rpbmcgd2l0aCB0aGUgYXBwIHsjaW50ZXJhY3R9CgpPbmNlIHRoZSBhcHAgaXMgbGF1bmNoZWQgeW91IGNhbiBzdGFydCBpbnRlcmFjdGluZyB3aXRoIGl0LgoKVGhlcmUgYXJlIG11bHRpcGxlIHRhYnMgdG8gZ28gdGhyb3VnaC4gClNvbWUgdGFicyB3aWxsIGJlIHNraXBwZWQgYXV0b21hdGljYWxseSBpZiB0aGV5IGRvbid0IGFwcGx5IHRvIHlvdXIgc2l0dWF0aW9uIGFuZCB5b3UgbWF5IHNraXAgb3RoZXJzIGlmIHlvdSBkb24ndCBuZWVkL3dhbnQgdGhlbS4KCldoZW4geW91IGxhbmQgb24gYSB0YWIsICoqYWx3YXlzIGFkdmFuY2Ugd2l0aCBhbiBhY3Rpb24gYnV0dG9uIChldmVuIGlmIHNraXBwaW5nKSBzbyB5b3VyIGlucHV0cyBhcmUgdGFrZW4gaW50byBhY2NvdW50KiouIFlvdSBtYXkgdXNlIHRoZSBuYXZpZ2F0aW9uIHBhbmVsIHRvIHJldHVybiB0byBhIHByZXZpb3VzIHRhYiBidXQgcmVtZW1iZXIgdG8gY2xpY2sgb24gYW4gYWN0aW9uIGJ1dHRvbiB0byBzYXZlIHlvdXIgdXBkYXRlZCBlbnRyaWVzLgoKCgojIyBVcGxvYWQgeW91ciBmaWxlKHMpIHsjdXBsb2FkfQoKVGhpcyB0YWIgc3RhcnRzIHdpdGggaW5mb3JtYXRpb24gdGhhdCB3ZSBhbHJlYWR5IGNvdmVyZWQgaW4gdGhlIFtpbnRyb10oI2ludHJvKS4gVGhlIGNoZWNrbGlzdCBpcyBvbmx5IGEgZ3VpZGVsaW5lIHRvIGhlbHAgeW91IGdldHRpbmcgcmVhZHksIGFuZCB5b3UgZG9uJ3QgYWN0dWFsbHkgbmVlZCB0byBjaGVjayB0aGUgYm94ZXMgdG8ga2VlcCBnb2luZy4KClRoZSBudW1iZXJlZCB0YXNrcyBhcmUgdGhlIGVsZW1lbnRzIHRoYXQgeW91IGRvIG5lZWQgdG8gY29tcGxldGUgdG8gYmUgYWJsZSB0byBtb3ZlIGZvcndhcmQuCgogMS4gSW5kaWNhdGUgaG93IG1hbnkgdGFibGVzIHlvdSB3aXNoIHRvIHVwbG9hZAogMi4gSW5kaWNhdGUgdGhlIGZpbmVzdCBsZXZlbCBvZiBtZWFzdXJlbWVudCBpbiB5b3VyIGRhdGE6CiAKICAgIC0gUGxvdDogaWYgeW91ciBkYXRhIG9ubHkgY29uc2lzdHMgb2YgcGxvdCBsZXZlbCBtZWFzdXJlbWVudHMgbGlrZSBzcGVjaWVzIHJpY2huZXNzLCB0b3RhbCBiYXNhbCBhcmVhLCB0b3RhbCBudW1iZXIgb2Ygc3RlbXMgZXRjLi4uCiAgICAtIFNwZWNpZXM6IGlmIHlvdXIgZGF0YSBjb25zaXN0cyBvZiBzcGVjaWVzIGxldmVsIG1lYXN1cmVtZW50cyBsaWtlIGFidW5kYW5jZSwgYmFzYWwgYXJlYSBldGMuLi4gVGhpcyBkb2VzIG5vdCBwcmV2ZW50IHlvdSBmcm9tIGFsc28gdXBsb2FkaW5nIHBsb3QgbGV2ZWwgaW5mb3JtYXRpb24gdGhhdCBpcyBzdG9yZWQgaW4gYSBzZXBhcmF0ZSBmaWxlLgogICAgLSBUcmVlOiBpZiB5b3VyIGRhdGEgY29uc2lzdHMgb2YgdHJlZSBkaWFtZXRlcnMsIGNpcmN1bWZlcmVuY2UsLi4uIGFuZCB5b3UgYXJlIG9ubHkgbWVhc3VyaW5nIHRoZSBtYWluIHN0ZW0gb2YgZWFjaCB0cmVlLiBUaGlzIGRvZXMgbm90IHByZXZlbnQgeW91IGZyb20gYWxzbyB1cGxvYWRpbmcgcGxvdCBhbmQgc3BlY2llcyBsZXZlbCBpbmZvcm1hdGlvbiBpZiB0aGV5IGFyZSBzdG9yZWQgaW4gc2VwYXJhdGUgZmlsZShzKS4KICAgIC0gU3RlbTogaWYgeW91ciBkYXRhIGNvbnNpc3RzIG9mIHN0ZW0gZGlhbWV0ZXJzLCBjaXJjdW1mZXJlbmNlLC4uLiBhbmQgc29tZSBzdGVtcyBiZWxvbmcgdG8gYSBzYW1lIHRyZWUuIFRoaXMgZG9lcyBub3QgcHJldmVudCB5b3UgZnJvbSB1cGxvYWRpbmcgcGxvdCBhbmQgc3BlY2llcyBsZXZlbCBpbmZvcm1hdGlvbiBpZiB0aGV5IGFyZSBzdG9yZWQgaW4gYSBzZXBhcmF0ZSBmaWxlKHMpLgogCkFnYWluLCBldmVuIGlmIHlvdSBhcmUgdXBsb2FkaW5nIHBsb3QgbGV2ZWwgaW5mb3JtYXRpb24gYnV0IGhhdmUgYSBzdGVtIGxldmVsIGRhdGEsIHlvdSBzaG91bGQgdXBsb2FkIHRoYXQgZmlsZSBhbG9uZyBhbmQgaW5kaWNhdGUgdGhhdCB5b3VyIGxldmVsIG9mIG1lYXN1cmVtZW50IGlzICJTdGVtIi4KCiAzLiBVcGxvYWQgeW91IHRhYmxlcy4gWW91J2xsIGhhdmUgYXMgbWFueSB1cGxvYWQgYm94ZXMgYXMgeW91IGluZGljYXRlZCBuZWVkaW5nIGluIHN0ZXAgMS4gRm9yIGVhY2ggb2YgdGhlbToKIAogICAgLSBjbGljayBvbiBgQnJvd3NlLi4uYCBhbmQgbmF2aWdhdGUgdG8gdGhlIGNzdiBmaWxlIHlvdSB3YW50IHRvIHVwbG9hZC4KICAgIC0gVHlwZSBhIG1vcmUgbWVhbmluZ2Z1bCBuYW1lIHRvIHJlcGxhY2UgdGhlIGdlbmVyaWMgIlRhYmxlMSIsICJUYWJsZTIiIGV0Yy4uLiBUaGlzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgaWYgeW91IGFyZSB1cGxvYWRpbmcgbW9yZSB0aGFuIG9uZSBmaWxlLgogICAgLSBDaGVjayBvbiB0aGUgcmlnaHQgaGFuZCBzaWRlIHRoYXQgdGhlIGNvbHVtbnMgYW5kIHJvd3Mgb2YgeW91ciBkYXRhIGFyZSByZW5kZXJpbmcgcHJvcGVybHkuIAogICAgLSBJbiB0aGUgdW5saWtlbHkgZXZlbnQgdGhhdCB5b3VyIHRhYmxlcyBhcmUgbm90IHJlbmRlcmluZyBwcm9wZXJseSwgYWRqdXN0IHRoZSBwYXJhbWV0ZXJzIChzZXBhcmF0b3IgYW5kIGhlYWRlcikgYnkgY2xpY2tpbmcgb24gdGhlIGxpdHRsZSBnZWFyIGljb24gYHIgZmEobmFtZSA9ICJjb2ciKWAuCgogNC4gKipDbGljayBvbiBTVUJNSVQqKiB0byBwcm9jZWVkIHRvIHRoZSBuZXh0IHN0ZXAuCiAKICAgICAKIVtdKFVwbG9hZC5naWYpCgoKIyMgU3RhY2sgdGFibGVzIHsjU3RhY2tpbmd9CgpJZiB5b3UgdXBsb2FkZWQgbW9yZSB0aGFuIG9uZSB0YWJsZSwgeW91IHdpbGwgYmUgcHJvbXB0ZWQgdG8gdGhlIGBTdGFjayB0YWJsZXNgIHRhYiwgYnV0IHRoaXMgdGFiIHdpbGwgYmUgc2tpcHBlZCBpZiB5b3Ugb25seSB1cGxvYWRlZCBvbmUgdGFibGUuCgpZb3Ugd2lsbCBuZWVkIHRvIHN0YWNrIDIgb3IgbW9yZSB0YWJsZXMgaWYgeW91IGFyZSBjb2xsZWN0aW5nIHRoZSBzYW1lIGluZm9ybWF0aW9uIGluIG11bHRpcGxlIGZpbGVzLiBUaGlzIGNhbiBiZSB0aGUgY2FzZSBpZiwgZm9yIGV4YW1wbGUsIHlvdSBhcmUga2VlcGluZyB5b3VyIG1lYXN1cmVtZW50cyBmcm9tIGRpZmZlcmVudCBwbG90cyBpbiBkaWZmZXJlbnQgZmlsZXMuIE9yIHlvdSBhcmUga2VlcGluZyBvbmUgZmlsZSBwZXIgY2Vuc3VzLgoKSWYgeW91IGRvbid0IG5lZWQgdG8gc3RhY2sgdGFibGVzLCAqKmNsaWNrIG9uIFNLSVAgVEhJUyBTVEVQKiouCgoqKkl0IGlzIGltcG9ydGFudCB0aGF0IHRoZSBmaWxlcyB5b3UgYXJlIHN0YWNraW5nIGhhdmUgdGhlIHNhbWUgc2V0IG9mIGNvbHVtbnMuKiogCgogMS4gU2VsZWN0IGFsbCB0aGUgdGFibGVzIHRoYXQgbmVlZCB0byBiZSBzdGFja2VkCiAKIDIuICoqQ2xpY2sgb24gU1RBQ0sgVEFCTEVTKioKIAogMy4gRG91YmxlIGNoZWNrIHlvdXIgbmV3bHkgY3JlYXRlZCB0YWJsZSBsb29rcyBvawogCiA0LiAqKkNsaWNrIG9uIEdPIFRPIE1FUkdFKiogdG8gcHJvY2VlZCB0byB0aGUgbmV4dCBzdGVwLiAoTm90ZTogaWYgeW91IGFyZSBkb3duIHRvIG9uZSB0YWJsZSBhdCB0aGlzIHN0YWdlIHRoZSBidXR0b24ncyBsYWJlbCB3aWxsIGNoYW5nZSwgc28gKipjbGljayBTS0lQIE1FUkdJTkcgU0lOQ0UgQUxMIFlPVVIgREFUQSBJUyBOT1cgU1RBQ0tFRCoqKS4KCgoKIVtdKFN0YWNrLmdpZikKCiMjIE1lcmdlIHRhYmxlcyB7I01lcmdpbmd9CgpJZiB5b3UgdXBsb2FkZWQgbW9yZSB0aGFuIG9uZSB0YWJsZSBhbmQgbm90IGFsbCBvZiB0aGVtIHdlcmUgc3RhY2tlZCwgeW91IHdpbGwgYmUgcHJvbXB0ZWQgdG8gdGhlIGBNZXJnZSB0YWJsZXNgIHRhYiwgYnV0IHRoaXMgdGFiIHdpbGwgYmUgc2tpcHBlZCBpZiB5b3Ugb25seSB1cGxvYWRlZCBvbmUgdGFibGUsIG9yIGlmIGFsbCB5b3VyIHRhYmxlcyB3ZXJlIHN0YWNrZWQuCgpBdCB0aGUgZW5kIG9mIHRoaXMgc3RhZ2UgeW91IGhhdmUgdG8gYmUgZG93biB0byBvbmUgdGFibGUuCgpZb3UgbmVlZCB0byB1c2UgbWVyZ2luZyBpZiwgZS5nLiwgeW91ciBzcGVjaWVzIG9yIHlvdXIgcGxvdCBpbmZvcm1hdGlvbiBpcyBzdG9yZWQgaW4gYSBkaWZmZXJlbnQgdGFibGUgdGhhbiB5b3VyIG1lYXN1cmVtZW50IHRhYmxlLCBhbmQgdGhlcmUgaXMgYXQgbGVhc3Qgb25lICJrZXkiIGNvbHVtbiB0aGF0IHlvdSBjYW4gdXNlIHRvIGNvbm5lY3QgdGhlIHRhYmxlcyB0b2dldGhlci4KCgogCiAxLiBJbiAqKk1lcmdlIHRoaXMgdGFibGUqKiwgc2VsZWN0IHRoZSBtYWluIG1lYXN1cmVtZW50IHRhYmxlICh0aGUgb25lIG9udG8gd2hpY2ggeW91IHdhbnQgdG8gbWVyZ2UgZXh0cmEgaW5mb3JtYXRpb24gaW50bywgZnJvbSBvdGhlciB0YWJsZXMpLiBOb3RlIHRoYXQgdGhpcyBtYXkgYmUgeW91ciBub3cgc3RhY2tlZCB0YWJsZS4KIAogMi4gSW4gKipBbmQgdGhpcyB0YWJsZSoqLCBTZWxlY3QgdGhlIHRhYmxlIHRoYXQgeW91IHdhbnQgdG8gYnJpbmcgaW5mb3JtYXRpb24gZnJvbS4KIAogMy4gPHNwYW4gc3R5bGU9ImNvbG9yOiMwMGMwZWY7Ij5DbGljayBvbiBib3RoIGJsdWUgYXJyb3dzLjwvc3Bhbj4KIAogNC4gSW4gdGhlIHR3byBkcm9wZG93biBtZW51cyBhYm91dCB0aGUgJ0tFWSBjb2x1bW4ocyknLCBzZWxlY3QgdGhlIGNvbHVtbihzKSB0aGF0IGFsbG93IHRvIGNvbm5lY3QgdGhlIHRvIHRhYmxlcyB0b2dldGhlci4qKlNlbGVjdCBhbGwgY29sdW1ucyB0aGF0IGFyZSBjb21tb24gYmV0d2VlbiB0YWJsZXMsIG90aGVyd2lzZSBjb2x1bW5zIHdpbGwgYmUgcmVwZWF0ZWQgaW4gdGhlIG91dHB1dCwgd2l0aCBleHRlbnNpb24gJy55JyBpbiB0aGUgbmFtZSBvZiB0aGUgc2Vjb25kIHRhYmxlLioqCiAKIDUuICoqY2xpY2sgb24gJ01FUkdFIFRBQkxFUycuKioKIAogNi4gSWYgeW91IGFyZSBzdGlsbCBub3QgZG93biB0byBvbmUgdGFibGUsIGFub3RoZXIgYm94IHdpbGwgYXBwZWFyLiBSZXBlYXQgMS01IHdpdGggdGhlIHJlbWFpbmluZyB0YWJsZXMuCiAKIDcuICoqY2xpY2sgb24gJ0dPIFRPIFRJRFknLioqCiAKCiFbXShNZXJnZS5naWYpCgojIyBUaWR5IHRhYmxlIHsjVGlkeWluZ30KCgpBdCB0aGlzIHN0YWdlLCB3ZSB3YW50IHRvIG1ha2Ugc3VyZSB5b3VyIGRhdGEgaGFzICoqb25lIHJvdyBwZXIgb2JzZXJ2YXRpb24gYW5kIG9uZSB2YXJpYWJsZSBwZXIgY29sdW1uLioqCgpJZiB5b3UgY29sbGVjdGVkIHRoZSBzYW1lIHR5cGUgb2YgaW5mb3JtYXRpb24gaW4gc2V2ZXJhbCBjb2x1bW5zIChlLmcuIHlvdSBhZGRlZCBhIGNvbHVtbiBlYWNoIHRpbWUgeW91IHZpc2l0ZWQgYSB0cmVlLCBvciBmb3IgZWFjaCBzdGVtIG9mIHRoZSB0cmVlIGV0Yy4uLiksIHlvdSBuZWVkIHRvICJ0aWR5IiB5b3VyIHRhYmxlIChhbHNvIGNhbGxlZCB3aWRlLXRvLWxvbmcgcmVzaGFwaW5nKS4gCgogMS4gSW4gdGhlIHRvcC1tb3N0IGJveCwgdXNlIHRoZSByYWRpby1idXR0b25zIHRvIGluZGljYXRlIHRoZSByZWFzb24geW91IGFkZGVkIG5ldyBjb2x1bW5zIGZvciBhIG5ldyBvYnNlcnZhdGlvbi4KIAogMi4gVGhlIG5leHQgc2V0IG9mIGJveGVzIGFyZSBwcmUtZmlsbGVkIHdpdGggb3VyIGJlc3QgZ3Vlc3NlcyBhdCB0aGUgY29sdW1ucyB0aGF0IG1heSBjb250YWluIHRoZSBzYW1lIHZhcmlhYmxlIChjb2x1bW5zIHRoYXQgaGF2ZSBzaW1pbGFyIG5hbWVzIGxpa2UgYGRiaDFgLCBgZGJoMmAsIG9yICBgeWVhcjFgLCBgeWVhcjJgLi4uKS4gT3VyIGd1ZXNzIG1heSBiZSB0ZXJyaWJsZS4gWW91ciByb2xlIGlzIHRvOgogCiAgICAgIGEuIEluZGljYXRlIHRoZSBuYW1lIG9mIHRoZSBuZXcgY29sdW1uIHRoYXQgeW91IHdoaWNoIHlvdXIgdmFyaWFibGUgdG8gYmUgY2FsbGVkIChlLmcuIGBkYmhgKSBpbiB0aGUgdGV4dCBib3guIE5vdGUgdGhhdCB0aGlzIHNob3VsZCBzdGFydCBieSBhIGxldHRlciBhbmQgaGF2ZSBubyBzcGFjZS4KICAgICAgIAogICAgICBiLiBTZWxlY3QgYWxsIHRoZSBjb2x1bW5zIG9mIHlvdXIgZGF0YSB0aGF0IHJlcHJlc2VudCB0aGUgdmFyaWFibGUgaW5kaWNhdGVkIGluIHN0ZXAgYS4gKGUuZyBgZGJoMWAgYW5kIGBkYmgyYCkgdXNpbmcgdGhlIGRyb3AtZG93biBtZW51LgoKICAgICAgYy4gVGljayB0aGUgbGl0dGxlIHRpY2stYm94IG9uIHRoZSB1cHBlci1sZWZ0IGNvcm5lciBvZiB0aGUgYm94LCB0byBpbmRpY2F0ZWQgdGhhdCB5b3UgZG8gd2FudCB0byB0YWtlIGludG8gYWNjb3VudCB3aGF0IHlvdSBzZWxlY3RlZC4KICAgICAgIAogMy4gUmVwZWF0IGEtYyBmb3IgdGhlIG5leHQgdmFyaWFibGUocyksIGUuZy4geW91IG1heSBuZWVkIGEgYm94IHRvIGluZGljYXRlIGB5ZWFyYCBpbiB0aGUgdGV4dCBib3ggYW5kIGB5ZWFyMWAgYW5kIGB5ZWFyMmAgaW4gdGhlIGRyb3AtZG93biBtZW51LiBEb24ndCBmb3JnZXQgdG8gdGljayB0aGUgdGljay1ib3ggZm9yIHRob3NlIHZhcmlhYmxlcyB0b28uCiAKIDQuICoqQ2xpY2sgb24gJ1RJRFknKioKIAogNS4gKipDbGljayBvbiAnR08gVE8gSEVBREVSUycqKgoKIVtdKFRpZHkuZ2lmKQoKIyMgSGVhZGVycyBhbmQgVW5pdHMgeyNIZWFkZXJzfQoKSGVyZSwgd2Ugd2FudCB0byBrbm93IGluIHdoYXQgY29sdW1uIHNvbWUga2V5IGluZm9ybWF0aW9uIGlzIHN0b3JlZC4KCiAxLiAqKklmLi4uKioKCiAgICAgLSAqKkEuIC4uLiB5b3VyIGRhdGEgZm9sbG93cyBvbmUgb2Ygb3VyIHByZS1sb2FkZWQgc3RhbmRhcmRzOioqIFlvdSBjYW4gc2VsZWN0IGl0IHRvIGhlbHAgZmlsaW5nIG91dCBzb21lIG9mIHRoZSBnZW5lcmFsIGluZm9ybWF0aW9uLiBCdXQgeW91IHNob3VsZCBkb3VibGUgY2hlY2sgdGhhdCBhY2N1cmF0ZWx5IGRlc2NyaWJlIHlvdXIgcGFydGljdWxhciBkYXRhIHNldC4KICAgICAtICoqQi4gLi4uIHlvdSBoYXZlIGFscmVhZHkgZ29uZSB0aHJvdWdoIHRoaXMgc3RlcCBhbmQgc2F2ZWQgeW91ciBwcm9maWxlICgucmRzIGZpbGUpOioqIFlvdSBjYW4gdXBsb2FkIHlvdXIgcHJvZmlsZS4gRG91YmxlIGNoZWNrIHRoYXQgdGhlIGluZm9ybWF0aW9uIGlzIGZpbGxlZCBvdXQgcHJvcGVybHkuCiAgICAgLSAqKkMuIC4uLlRoaXMgaXMgeW91ciBmaXJzdCB0aW1lIG9uIHRoaXMgcGFnZToqKiBZb3UgaGF2ZSB0byBnbyB0aHJvdWdoIGFsbCB0aGUgZHJvcC1kb3duIG1lbnVzOgogICAgICAgLSBjb2x1bW4gMTogR28gdGhyb3VnaCB0aGlzIGNvbHVtbiBiZWZvcmUgaW5kaWNhdGluZyBtb3JlIGluZm9ybWF0aW9uIGluIGNvbHVtbiAyLiBGb3IgZWFjaCBlbGVtZW50LCBzZWxlY3QgdGhlIG5hbWUgb2YgdGhlIGZpZWxkIG9mIHlvdXIgZGF0YSwgaWYgYW55LCB0aGF0IGNvcnJlc3BvbmRzIHRoZSBiZXN0IHRvIHdoYXQgaXMgYXNrZWQuIExlYXZlICJub25lIiBpZiBub25lIG9mIHlvdXIgdmFyaWFibGUgYXBwbHkuCiAgICAgICAtIGNvbHVtbiAyOiBDb21wbGV0ZSB0aGUgaW5mb3JtYXRpb24gb2YgdGhhdCBjb2x1bW4gYWZ0ZXIgeW91IGFyZSBkb25lIHdpdGggdGhlIGluZm9ybWF0aW9uIG9mIGNvbHVtbiAxLCBiZWNhdXNlIHNvbWUgZmllbGQgbWF5IGFwcGVhci9kaXNhcHBlYXIgZGVwZW5kaW5nIG9uIHdoYXQgeW91IGVudGVyZWQgaW4gMS4KICAgICAgIC0gT25jZSB5b3UgYXJlIGRvbmUsIHNhdmUgeW91ciBwcm9maWxlIChhIC5yZHMgZmlsZSkgc28geW91IHdvbid0IG5lZWQgdG8gZ28gdGhyb3VnaCBhbGwgb2YgdGhpcyBhZ2FpbiAoc2VlIEIuIGFib3ZlKSAKICAgICAgIAogMi4gKipjbGljayBvbiAnQVBQTFkgQ0hBTkdFUycqKiBhbmQgcmVhZCBhbnkgd2FybmluZ3MgdGhhdCBtYXkgcG9wdXAsIGFkanVzdCB5b3VyIGVudHJpZXMgaWYgcG9zc2libGUgKGl0IGlzIG9rYXkgdG8gaWdub3JlIHdhcm5pbmdzKSBhbmQgcmUtYXBwbHkgeW91ciBjaGFuZ2VzLiBTYXZlIChvciB1cGRhdGUgaWYgbmVlZGVkKSB5b3VyIHByb2ZpbGUgKC5yZHMgZmlsZSkKCiAzLiBDaGVjayB5b3VyIG5ldyBmb3JtYXRlZCBkYXRhIGxvb2tzIG9rLiBUaGUgaGVhZGVycyBhbmQgdW5pdHMgYXJlIG5vdyBmb2xsb3dpbmcgQVRGUydzIHN0YW5kYXJkLiBZb3UgY2FuIHNlZSB3aGF0IHRob3NlIGFyZSBieSBjbGlja2luZyB0aGUgbGl0dGxlIGByIGZhKG5hbWUgPSAiaW5mby1jaXJjbGUiKWAgYnV0dG9uLgogCiA1LiAqKmNsaWNrIG9uICdORVhUJyoqCgoKIVtdKEhlYWRlcnMuZ2lmKQoKIyMgQ29kZXMgeyNDb2Rlc30KClRoaXMgc3RhZ2UgaXMgb25seSByZXF1aXJlZCBpZiB5b3UgaW5kaWNhdGVkIGNvbHVtbihzKSBmb3IgdHJlZSBjb2RlcyBpbiB0aGUgVHJlZSBNZWFzdXJlbWVudCBzZWN0aW9uIG9mIHRoZSBwcmV2aW91cyB0YWIuCgpUaGUgdGFibGUgc2hvd3MgdGhlIGxpc3Qgb2YgY29kZXMgdGhhdCBhcmUgYXZhaWxhYmxlIGluIHRoZSBjb2x1bW4ocykgeW91IGluZGljYXRlZC4KSWYgeW91IGludGVuZCB0byB0cmFuc2xhdGUgdGhlc2UgY29kZXMgdG8gbWF0Y2ggdGhlIG9uZXMgb2YgYW5vdGhlciBwcm9maWxlICh3aGljaCB5b3Ugd2lsbCBiZSBhYmxlIHRvIGRvIGF0IGEgbGF0ZXIgc3RlcCksIG9yIHZpY2UtdmVyc2EsIHlvdSBuZWVkIHRvIGZpbGwgdGhpcyB0YWJsZSBvdXQuCgpPbmNlIHlvdSBhcmUgZG9uZSB3aXRoIHRoZSB0YWJsZSB5b3UgY2FuIHVwZGF0ZSB5b3VyIHByb2ZpbGUgKGJ5IGRvd25sb2FkaW5nIGFuZCBvdmVyd3JpdGluZyB5b3VyIC5yZHMgZmlsZSkuIElmIHlvdSBoYXZlIGFscmVhZHkgc2F2ZWQgeW91ciBwcm9maWxlIGFmdGVyIHRoaXMgc3RlcCBhbmQgdXNlZCBpdCB0byBmaWxsIG91dCB0aGUgcHJldmlvdXMgc3RlcCwgeW91IGNhbiBjbGljayBvbiB0aGUgIlVzZSB5b3VyIHByb2ZpbGUiIGJ1dHRvbiB0byBhdXRvbWF0aWNhbGx5IGZpbGwgdGhlIHRhYmxlLgoKClRoZXJlIGlzIGEgbGlzdCBvZiBwcmVkZWZpbmVkIGRlZmluaXRpb25zLCB3aGljaCwgaWYgdXNlZCBieSB5b3UgYW5kIHlvdXIgY29sbGFib3JhdG9yLCB3aWxsIGhlbHBzIGF1dG9tYXRpY2FsbHkgdHJhbnNsYXRpbmcgeW91ciBjb2Rlcy4gQnV0IGlmIHlvdSBjYW4ndCBmaW5kIGEgZGVmaW5pdGlvbiB0aGF0IG1hdGNoZXMgeW91cnMsIGp1c3QgdHlwZSB5b3VyIG93bi4KCgohW10oQ29kZXMuZ2lmKQoKIyMgQ29ycmVjdGlvbnMgeyNDb3JyZWN0fQoKIyMgT3V0cHV0IGZvcm1hdCB7I091dHB1dEZvcm1hdH0KCiMjIFZpc3VhbGlzZSByZXN1bHRzIHsjVmlzdWFsaXNlfQoKIyMgRG93bmxvYWQgeyNTYXZlfQoKIyMgSGVscCB7I0hlbHB9CgoK